package pe.com.cotelasa.common.dao;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import pe.com.cotelasa.common.dao.excepcion.DAOException;
import pe.com.cotelasa.scp.dominio.common.ESTADO_REGISTRO;


@SuppressWarnings({"unchecked", "rawtypes"})
@Repository("daoImpl")
public class DaoImpl<Entidad, id extends Serializable> implements IDao<Entidad, id> {

    @Autowired
    protected SessionFactory sessionFactory;

    @Override
    @Transactional
    public void insertar(Entidad entidad) throws DAOException {
        try {
            sessionFactory.getCurrentSession().persist(entidad);
            sessionFactory.getCurrentSession().flush();
            sessionFactory.getCurrentSession().evict(entidad);
        } catch (Exception e) {
            throw new DAOException(e);
        }
    }
    
    @Override
    @Transactional
    public void insertarColeccion(List<Entidad> listaEntidad)
    		throws DAOException {
    	for (Entidad entidad : listaEntidad) {
			insert(entidad);
		} 
    }
    
    
    @Override
    @Transactional
    public void actualizar(final Entidad entidad) throws DAOException {
        try {
            sessionFactory.getCurrentSession().merge(entidad);
            sessionFactory.getCurrentSession().flush();
            sessionFactory.getCurrentSession().evict(entidad);
        } catch (Exception e) {
            throw new DAOException(e);
        }
    }
 

    @Transactional
    @Override
    public void eliminar(final Entidad entidad) throws DAOException {
        try {
            sessionFactory.getCurrentSession().flush();
            sessionFactory.getCurrentSession().delete(entidad);
        } catch (Exception e) {
            throw new DAOException(e);
        }
    }
    
    @Override
    @Transactional
    public void eliminarColeccion(List<Entidad> listaEntidad) throws DAOException {
    	for (Entidad entidad : listaEntidad) {
    		sessionFactory.getCurrentSession().flush();
        	sessionFactory.getCurrentSession().delete(entidad);
		}
    }

    @Transactional
    @Override
    public List<Entidad> listarTodos(final Class clase) throws DAOException {
        List<Entidad> listaEntidad = new ArrayList<Entidad>();
        try {
            sessionFactory.getCurrentSession().flush();
            listaEntidad = sessionFactory.getCurrentSession().createCriteria(clase).list();
            return listaEntidad;
        } catch (Exception e) {
            throw new DAOException(e);
        }
    }

    @Transactional
    @Override
    public Collection<Entidad> listarActivos(final Class clase) throws DAOException {
        try {
        	Collection<Entidad> listaEntidad = null;
            sessionFactory.getCurrentSession().flush();
            listaEntidad = sessionFactory.getCurrentSession().createCriteria(clase)
                    .add(Restrictions.eq("estadoRegistro", ESTADO_REGISTRO.ACTIVO.getValor()))
                    .addOrder(Order.desc("id"))
                    .list();
            return listaEntidad;
        } catch (Exception e) {
            throw new DAOException(e);
        }
    }
 
    @Transactional
    @Override
    public Entidad obtenerEntidadPorId(Class clase, Serializable id) throws DAOException {
        try {
            Entidad ent = (Entidad) sessionFactory.getCurrentSession().createCriteria(clase).add(Restrictions.idEq(id)).uniqueResult();
            return ent;
        } catch (Exception e) {
            throw new DAOException(e);
        }
    }

    
    @Override
    @Transactional
	public id conseguirUltimoId(Class<Entidad> clase) throws DAOException {
		try {
			id Id = (id) sessionFactory
					.getCurrentSession()
					.createCriteria(clase)
					.setProjection(Projections.max("id"))					
					.uniqueResult();
			if(Id == null){
				Id = (id) new Integer(0);
			}
			return Id;			
		} catch (Exception e) {
			throw new DAOException(e);
		}
	}

    @SuppressWarnings("deprecation")
    @Override
	@Transactional
    public Collection<Entidad> obtenerColeccionEntidadPorFiltros(Class<Entidad> clase,
            Map<String, Object> parametros) throws DAOException {
        try {
            List<Entidad> list = null;
            String criterio = " ";
            String query = "from " + clase.getSimpleName() + " ";
            if (parametros != null && !parametros.isEmpty()) {
                criterio = " where ";
                for (Entry<String, Object> entri : parametros.entrySet()) {
                    if (entri.getValue() instanceof String) {
                        criterio = criterio + " " + entri.getKey() + " like '%"
                                + entri.getValue() + "%' and";
                    } else if(entri.getValue() instanceof Character){
                        criterio = criterio + " " + entri.getKey() + " = '"
                                + entri.getValue() + "' and";
                    }else{
                        criterio = criterio + " " + entri.getKey() + " = "
                                + entri.getValue() + " and";
                    }
                }
                criterio = criterio.substring(0, criterio.length() - 3);
                if(!clase.getSimpleName().startsWith("Detalle")){
                    criterio+= "ORDER BY id DESC";
                }
                

            } else {
                parametros = null;
            }
            sessionFactory.getCurrentSession().flush();
            System.out.println(query + (criterio == null ? "" : criterio));
            list = sessionFactory.getCurrentSession().find(query + (criterio == null ? "" : criterio));
            System.out.println(query + criterio);
            return list;
        } catch (Exception e) {
            throw new DAOException(e);
        }
    }

    @SuppressWarnings("deprecation")
    @Override
	@Transactional
    public Collection<Entidad> obtenerColeccionEntidadPorFiltros(Class<Entidad> clase,
            Map<String, Object> parametros,String campoOrdenar,String tipo) throws DAOException {
        try {
            List<Entidad> list = null;
            String criterio = " ";
            String query = "from " + clase.getSimpleName() + " ";
            if (parametros != null && !parametros.isEmpty()) {
                criterio = " where ";
                for (Entry<String, Object> entri : parametros.entrySet()) {
                    if (entri.getValue() instanceof String) {
                        criterio = criterio + " " + entri.getKey() + " like '%"
                                + entri.getValue() + "%' and";
                    } else if(entri.getValue() instanceof Character){
                        criterio = criterio + " " + entri.getKey() + " = '"
                                + entri.getValue() + "' and";
                    }else{
                        criterio = criterio + " " + entri.getKey() + " = "
                                + entri.getValue() + " and";
                    }
                }
                criterio = criterio.substring(0, criterio.length() - 3);
                if(!clase.getSimpleName().startsWith("Detalle")){
                    criterio+= "ORDER BY " + campoOrdenar + " "+tipo;
                }
                

            } else {
                parametros = null;
            }
            sessionFactory.getCurrentSession().flush();
            System.out.println(query + (criterio == null ? "" : criterio));
            list = sessionFactory.getCurrentSession().find(query + (criterio == null ? "" : criterio));
            System.out.println(query + criterio);
            return list;
        } catch (Exception e) {
            throw new DAOException(e);
        }
    }
    
    @Override
    @Transactional
    public id insert(Entidad entidad) throws DAOException {
         try {
            Serializable pk=sessionFactory.getCurrentSession().save(entidad);
            sessionFactory.getCurrentSession().flush();
            sessionFactory.getCurrentSession().evict(entidad);
            return (id) pk;
        } catch (Exception e) {
            throw new DAOException(e);
        }
    }

    @Override
    @Transactional
    public void actualizarColeccion(List<Entidad> listaEntidad) throws DAOException {
    	for (Entidad entidad : listaEntidad) {
    		actualizar(entidad);
		}
    }
    
    @Override
    @Transactional
    public Collection<Entidad> obtenerColeccionCacheEntidadPorFiltros(
    		Class<Entidad> clase, Map<String, Object> parametros) throws DAOException {
    	List<Entidad> list = null;
    	String criterio = " ";
        String query = "from " + clase.getSimpleName() + " ";
        if (parametros != null && !parametros.isEmpty()) {
            criterio = " where ";
            for (Entry<String, Object> entri : parametros.entrySet()) {
                if (entri.getValue() instanceof String) {
                    criterio = criterio + " " + entri.getKey() + " like '%"
                            + entri.getValue() + "%' and";
                } else if(entri.getValue() instanceof Character){
                    criterio = criterio + " " + entri.getKey() + " = '"
                            + entri.getValue() + "' and";
                }else{
                    criterio = criterio + " " + entri.getKey() + " = "
                            + entri.getValue() + " and";
                }
            }
            criterio = criterio.substring(0, criterio.length() - 3);
            criterio+= "ORDER BY id DESC";

        } 
        
        Query _query = sessionFactory.getCurrentSession().createQuery(query + (criterio == null ? "" : criterio));
        _query.setCacheable(true);
        list = _query.list();
    	return list;
    }
   
}
